import random
import time
from typing import List
from common.utility import Utility
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed, Future, wait, FIRST_COMPLETED, ALL_COMPLETED

# 主函数
def main(max_workers: int | None = None, thread_count: int = 5):
    mode: str = input('''请选择ThreadPoolExecutor的使用模式：
    0-map and wait
    1-submit and as_completed
    2-submit and use sequentially
    3-submit and callback
    4-submit and wait for all
    5-submit and wait for first
    ''')
    Utility.print_info(f'entered mode {mode}.')
    with ThreadPoolExecutor(max_workers=max_workers, initializer=threadpool_init) as pool:
        sleep_time_list: List[int] = []
        for i in range(thread_count):
            sleep_time_list.append(random.randint(1,10))
        match mode:
            case '0':
                map_and_wait(pool, sleep_time_list)
            case '1':
                submit_and_as_completed(pool, sleep_time_list)
            case '2':
                submit_and_sequentially(pool, sleep_time_list)
            case '3':
                submit_and_callback(pool, sleep_time_list)
            case '4':
                submit_and_wait_all(pool, sleep_time_list)
            case '5':
                submit_and_wait_first(pool, sleep_time_list)
            case _:
                Utility.print_info('错误的模式选择。')

    Utility.print_info('exited.')

def map_and_wait(pool: ThreadPoolExecutor, sleep_time_list: List[int]) -> None:
    for result in pool.map(task, sleep_time_list):
        Utility.print_info(result)

def submit_and_as_completed(pool: ThreadPoolExecutor, sleep_time_list: List[int]) -> None:
    futures: List[Future[tuple[int, str, int]]] = [pool.submit(task, sleep_time) for sleep_time in sleep_time_list]
    for future in as_completed(futures):
        Utility.print_info(future.result())

def submit_and_sequentially(pool: ThreadPoolExecutor, sleep_time_list: List[int]) -> None:
    futures: List[Future[tuple[int, str, int]]] = [pool.submit(task, sleep_time) for sleep_time in sleep_time_list]
    for future in futures:
        Utility.print_info(future.result())

def submit_and_callback(pool: ThreadPoolExecutor, sleep_time_list: List[int]) -> None:
    futures: List[Future[tuple[int, str, int]]] = [pool.submit(task, sleep_time) for sleep_time in sleep_time_list]
    for future in futures:
        future.add_done_callback(done_callback)

def submit_and_wait_all(pool: ThreadPoolExecutor, sleep_time_list: List[int]) -> None:
    futures: List[Future[tuple[int, str, int]]] = [pool.submit(task, sleep_time) for sleep_time in sleep_time_list]
    done, not_done = wait(futures, return_when=ALL_COMPLETED)
    for future in done:
        Utility.print_info(f'线程成功执行完成，返回结果：{future.result()}')

def submit_and_wait_first(pool: ThreadPoolExecutor, sleep_time_list: List[int]) -> None:
    futures: List[Future[tuple[int, str, int]]] = [pool.submit(task, sleep_time) for sleep_time in sleep_time_list]
    done, not_done = wait(futures, return_when=FIRST_COMPLETED)
    for future in done:
        Utility.print_info(f'线程成功执行完成，返回结果：{future.result()}')
    Utility.print_info(f'未执行完的线程数：{len(not_done)}')
    for future in not_done:
        running: bool = future.running()
        cancelled: bool = future.cancelled()
        is_done: bool = future.done()
        exception: Exception = future.exception()
        Utility.print_info(f'未执行完的线程，是否正在运行：{running}，是否已取消：{cancelled}，是否已完成：{is_done}，异常：{exception}')
        if running:
            future.cancel()

# 线程池初始化
def threadpool_init() -> None:
    Utility.print_info('线程池初始化。')

# 线程完成回调
def done_callback(future: Future[tuple[int, str, int]]) -> None:
    Utility.print_info(f'线程执行完毕，返回结果：{future.result()}')

# 线程池回调函数
def callback(_) -> None:
    Utility.print_info(f'线程池回调函数。{_}')

# 线程池错误回调函数
def error_callback(ex: BaseException) -> None:
    Utility.print_info(f'线程池错误回调函数：{ex}')

# 任务
def task(sleep_time: int) -> tuple[int, str, int]:
    Utility.print_info(f'entered. It will sleep {sleep_time} seconds.')
    time.sleep(sleep_time)
    Utility.print_info('exited.')
    cur_thread: threading.Thread = threading.current_thread()
    return cur_thread.ident, cur_thread.name, sleep_time

main()
